#include "fyp/shadows/BiPCF2.h"

//------------------------------------------------------------------------------
// additional header includes
#include "fyp/ObjectManager.h"
#include "fyp/ResourcePool.h"

#include "fyp/render/Renderer.h"
#include "fyp/Gui.h"
#include "DXUT.h"
#include "DXUTgui.h"


using namespace ming::fyp::render;

//------------------------------------------------------------------------------
namespace ming
{
namespace fyp
{
namespace shadows
{
	//--------------------------------------------------------------------------
	// TBiPCF2Gui
	//--------------------------------------------------------------------------
	class TBiPCF2Gui : public GuiImpl_t<TBiPCF2>
	{
	public:
		enum CONTROL_ID
		{
			ID_FILTER_KERNEL_SIZE,
		};

		//------------------------------
		// #cstor & #dstor
		TBiPCF2Gui(TBiPCF2 *model, TGuiManager *guiMgr) :
			GuiImpl_t<TBiPCF2>(model, guiMgr, "BiPCF2")
		{
			mDlg->AddStatic(-1, L"Filter Kernel", 10, 180, 100, 20);
			mDlg->AddComboBox(ID_FILTER_KERNEL_SIZE, 120, 180, 120, 20);

			mDlg->GetComboBox(ID_FILTER_KERNEL_SIZE)->AddItem(L"2 x2", NULL);
			mDlg->GetComboBox(ID_FILTER_KERNEL_SIZE)->AddItem(L"3 x 3", NULL);
			mDlg->GetComboBox(ID_FILTER_KERNEL_SIZE)->AddItem(L"4 x 4", NULL);
			mDlg->GetComboBox(ID_FILTER_KERNEL_SIZE)->AddItem(L"8 x 8", NULL);

			mDlg->GetComboBox(ID_FILTER_KERNEL_SIZE)->SetSelectedByIndex( mModel->mCurrApplyTechnique);
		}

		~TBiPCF2Gui() 
		{
		}
		
		//------------------------------
		// external operations
		virtual void OnControlEvent(int controlID, CDXUTControl *control)
		{
			
			if(controlID == ID_FILTER_KERNEL_SIZE)
			{
				UpdateFilterKernelSize();
			}
		}

		static TBiPCF2Gui *inst;

	private:
		//------------------------------
		// internal attributes
		void UpdateFilterKernelSize()
		{
			mModel->mCurrApplyTechnique = (int)mDlg->GetComboBox(ID_FILTER_KERNEL_SIZE)->GetSelectedIndex();
		}
		
		//------------------------------
		// internal operations
	};

	TBiPCF2Gui *TBiPCF2Gui::inst = NULL;

	//--------------------------------------------------------------------------
	TBiPCF2::TBiPCF2()
	{
		mPendingShadowMapSize = 512;

		mApplyTechniques.push_back( STR("Apply2x2") );
		mApplyTechniques.push_back( STR("Apply3x3") );
		mApplyTechniques.push_back( STR("Apply4x4") );
		mApplyTechniques.push_back( STR("Apply8x8") );

		mCurrApplyTechnique = 1;
	}
	
	//--------------------------------------------------------------------------
	TBiPCF2::~TBiPCF2()
	{
	}

	//--------------------------------------------------------------------------
	void TBiPCF2::Init(TResourcePool *resPool, TGuiManager *guiMgr)
	{
		for(size_t i=0; i<mApplyTechniques.size(); ++i)
		{
			TStr &tech = mApplyTechniques[i];
			resPool->Add<IShader>("BiPCF2." + tech, make_tuple( STR("../media/shader/BiPCF2.Apply.fx"), tech ) );
		}

		resPool->Add<IShader>("BiPCF2.Generate", make_tuple( STR("../media/shader/BiPCF2.Generate.fx"), STR("Generate") ) );

		TBiPCF2Gui::inst = new TBiPCF2Gui(this, guiMgr);
	}

	//--------------------------------------------------------------------------
	void TBiPCF2::RenderScene(
		render::IRenderer *renderer,
		TResourcePool *resPool,
		TObjectManager *objMgr)
	{

		if(mPendingShadowMapSize > 0)
		{
			resPool->Remove<IRenderedTexture>("BiPCF2.SMap");

			resPool->Add<IRenderedTexture>(
				"BiPCF2.SMap", 
				make_tuple( (size_t)IRenderedTexture::FLOAT16_GR,
							(size_t)mPendingShadowMapSize,
							(size_t)mPendingShadowMapSize,
							true) );

			resPool->Get<IRenderedTexture>("BiPCF2.SMap")->SetFilterMode(
				ITexture::POINT, ITexture::POINT, ITexture::POINT);

			resPool->Get<IRenderedTexture>("BiPCF2.SMap")->SetAddressMode(
				ITexture::CLAMP, ITexture::CLAMP, ITexture::CLAMP);

			mPendingShadowMapSize = -1;
		}

		GenerateShadowMap(renderer, resPool, objMgr);

		RenderSceneWithShadowMap(renderer, resPool, objMgr);
	}
	
	//--------------------------------------------------------------------------
	void TBiPCF2::Final(TResourcePool *resPool)
	{
		delete TBiPCF2Gui::inst;

		for(size_t i=0; i<mApplyTechniques.size(); ++i)
		{
			TStr &tech = mApplyTechniques[i];
			resPool->Remove<IShader>("BiPCF2." + tech);
		}

		resPool->Remove<IShader>("BiPCF2.Generate");

		resPool->Remove<IRenderedTexture>("BiPCF2.SMap");
	}

	//--------------------------------------------------------------------------
	void TBiPCF2::ChangeShadowMapSize(size_t sz)
	{
		mPendingShadowMapSize = (int)sz;
	}

	//--------------------------------------------------------------------------
	const char* TBiPCF2::GetDescription()
	{
		return "Bilinear Percentage Closer Filtering";
	}

	//--------------------------------------------------------------------------
	void TBiPCF2::GenerateShadowMap(
		render::IRenderer *renderer,
		TResourcePool *resPool,
		TObjectManager *objMgr)
	{
		IShader *shader = resPool->Get<IShader>("BiPCF2.Generate");

		renderer->BindRenderTarget(resPool->Get<IRenderedTexture>("BiPCF2.SMap"), NULL, NULL, NULL, true);
		renderer->ClearColor( TVec4(1.0f, 1.0f, 1.0f, 1.0f) );
		renderer->ClearDepth(1.0f);

		objMgr->RenderCasterObject(renderer, shader);
		objMgr->RenderReceiverObject(renderer, shader);
	}

	//--------------------------------------------------------------------------
	void TBiPCF2::RenderSceneWithShadowMap(
		render::IRenderer *renderer,
		TResourcePool *resPool,
		TObjectManager *objMgr)
	{

		IShader *shader = resPool->Get<IShader>("BiPCF2." + mApplyTechniques[mCurrApplyTechnique]);

		renderer->RestoreDefaultRenderTarget();
		
		renderer->BindTexture(3, resPool->Get<IRenderedTexture>("BiPCF2.SMap") );

		objMgr->RenderCasterObject(renderer, shader);
		objMgr->RenderReceiverObject(renderer, shader);
		
		renderer->BindTexture(3, NULL);
	}
	
	//--------------------------------------------------------------------------
}
}
}
